/**
 *  https://javadoop.com/post/AbstractQueuedSynchronizer
 *  https://www.javadoop.com/post/AbstractQueuedSynchronizer-2
 *  https://www.javadoop.com/post/AbstractQueuedSynchronizer-3
 *  https://coderbee.net/index.php/concurrent/20131115/577
 *
 * AQS (AbstractQueuedSynchronizer) 结构
 *   头结点，你直接把它当做 当前持有锁的线程 可能是最好理解的
 *   private transient volatile Node head;
 *
 *   阻塞的尾节点，每个新的节点进来，都插入到最后，也就形成了一个链表
 *   private transient volatile Node tail;
 *
 *   这个是最重要的，代表当前锁的状态，0代表没有被占用，大于 0 代表有线程持有当前锁
 *   这个值可以大于 1，是因为锁可以重入，每次重入都加上 1
 *   private volatile int state;
 *
 *    代表当前持有独占锁的线程，举个最重要的使用例子，因为锁可以重入
 *    reentrantLock.lock()可以嵌套调用多次，所以每次用这个来判断当前线程是否已经拥有了锁
 *    if (currentThread == getExclusiveOwnerThread()) {state++}
 *    private transient Thread exclusiveOwnerThread; //继承自AbstractOwnableSynchronizer
 *
 * Node 的数据结构
 *   thread + waitStatus + pre + next 四个属性
 *
 *  ReentrantLock
 *
 *    Sync
 *     |
 *     |-- FairSync
 *     |-- NonfairSync
 *     ReentrantLock#lock => AbstractQueuedSynchronizer#acquire
 *     ReentrantLock#unlock => AbstractQueuedSynchronizer#release
 *
 *   1、 锁状态。我们要知道锁是不是被别的线程占有了，这个就是 state 的作用，它为 0 的时候代表没有线程占有锁，可以去争抢这个锁，用 CAS 将 state 设为 1，
 *       如果 CAS 成功，说明抢到了锁，这样其他线程就抢不到了，如果锁重入的话，state进行 +1 就可以，解锁就是减 1，直到 state 又变为 0，代表释放锁，
 *       所以 lock() 和 unlock() 必须要配对啊。然后唤醒等待队列中的第一个线程，让其来占有锁。
 *   2、 线程的阻塞和解除阻塞。AQS 中采用了 LockSupport.park(thread) 来挂起线程，用 unpark 来唤醒线程。
 *   3、 阻塞队列。因为争抢锁的线程可能很多，但是只能有一个线程拿到锁，其他的线程都必须等待，这个时候就需要一个 queue 来管理这些线程，
 *       AQS 用的是一个 FIFO 的队列，就是一个链表，每个 node 都持有后继节点的引用。
 *
 *   阻塞队列和条件队列
 *
 *  Condition/ConditionObject
 *    ConditionObject 只有两个属性 firstWaiter 和 lastWaiter；
 *    条件队列（condition queue）
 *
 *
 *   CountDownLatch/ CyclicBarrier /Semaphore
 *
 *    AQS实现
 *
 *   多阅读源码吧
 *
 */
package cn.hy.concurrent.AQS;